package org.carabiner.mockutil;

import org.jmock.Mock;
import org.jmock.core.CoreMock;
import org.jmock.core.DynamicMock;

/**
 * <code>BeanMock</code> dynamically manages java bean properties for a given
 * interface. It will automatically stub out all getter and setter methods in
 * the interface, and manage the properties behind those methods. It's usage is
 * simple. An example from the <code>BeanMock</code> unit test:
 * 
 * <code><pre>
 * Mock beanMock = new BeanMock(Bean.class);
 * Bean bean = (Bean) beanMock.proxy();
 * assertNull(bean.getColor());
 * bean.setColor(Color.RED);
 * assertEquals(Color.RED, bean.getColor());
 * </pre></code>
 * 
 * <b>Note:</b> If expectations have been set on the <code>BeanMock</code> as
 * they would be on the standard <code>Mock</code>, those expectations will
 * be honored in place of the dynamic <code>BeanMock</code> code. This allows
 * you to return something meaningful from a getter when the interface provides
 * no setter.
 * 
 * <b>See</b> the unit test <code>TestBeanMock</code> for more use cases.
 * 
 * @author <a href="mailto:bignate@gmail.com">Nathan Eror</a>
 * @version 0.3
 */
public class BeanMock extends Mock
{

  /**
   * Create a default <code>BeanMock</code> to mock the <code>clazz</code>
   * interface. Like in JMock, <code>clazz</code> must be an interface as 
   * the JDK's Proxy class is used to stub out the interface.
   * 
   * @param clazz
   *          Class
   */
  public BeanMock(Class clazz)
  {
    this(clazz, CoreMock.mockNameFromClass(clazz));
  }

  BeanMock(Class clazz, String name)
  {
    this(new CoreMock(clazz, name, new BeanInvocationDispatcher()));
  }

  /**
   * Create a <code>BeanMock</code>.</br> If
   * <code>returnDefaultPrimitiveValues</code> is set to true, any getter
   * methods that return primitives will return the default uninitialized values
   * for their type, 0 for number types like <code>int</code> and
   * <code>double</code>, and <code>false</code> for <code>boolean</code>.
   * This is in opposition to the default behavior of both <code>JMock</code>
   * and <code>BeanMock</code> which will throw a
   * <code>NullPointerException</code>. </br> Similarly,
   * <code>returnDefaultObjectInstances</code> will attempt to return an
   * <code>Object</code> instance for any <code>Object</code> return type. If the
   * <code>type</code> in question does not provide a default constructor, the
   * BeanMock will return <code>null</code>.
   *
   * @param clazz Class
   * @param name String
   * @param returnDefaultPrimitiveValues boolean
   * @param returnDefaultObjectInstances boolean
   */
  public BeanMock(Class clazz, boolean returnDefaultPrimitiveValues,
      boolean returnDefaultObjectInstances)
  {
    this(new CoreMock(clazz, CoreMock.mockNameFromClass(clazz),
        new BeanInvocationDispatcher(returnDefaultPrimitiveValues,
            returnDefaultObjectInstances)));
  }

  BeanMock(DynamicMock dynamicMock)
  {
    super(dynamicMock);
  }

  /**
   * Convenience method for creating an expectation for a {@link DynamicStub}.
   * Setting your <code>DynamicStub</code> expectation with this method will
   * allow you to use all features of the <code>BeanMock</code>,
   * <code>DynamicStub</code>, and <code>JMock</code>. When a method on
   * this <code>BeanMock</code> is invoked, execution will occur in this order
   * until one is successful.
   * <ol>
   * 
   * <li>Invocation will be attempted with the DynamicStub.</li>
   * 
   * <li>Invocation will fall back to the standard JMock expectation system.</li>
   * 
   * <li>Invocation will fall back to the <code>BeanMock</code> dynamic
   * getter/setter mocking code.</li>
   * 
   * <li>If none of that works, then an <code>AssertionFailedError</code>
   * will be thrown back to the caller.</li>
   * </ol>
   * 
   * @param dynamicStub
   *          DynamicStub
   */
  public void expectsDynamicStub(DynamicStub dynamicStub)
  {
    expects(new DynamicStubInvocationMatcher(dynamicStub)).will(dynamicStub);
  }
}
